
#include "apr.h"
#include "apr_network_io.h"
#include "apr_pools.h"
#include "apr_strings.h"

#define NEED_GET_SESSION 1
#define NEED_NOT_GET_SESSION 0

#define HEADER_TYPE    13

#define OPTION          1
#define DESCRIBE        2
#define ANNOUNCE        3
#define SETUP           4
#define PLAY            5
#define PAUSE           6
#define TEARDOWN        7
#define GET_PARAMETER   8
#define SET_PARAMETER   9
#define REDIRECT       10
#define RECORD         11
#define EMBEDDED       12


const char *HEADER_STRING[HEADER_TYPE] = 
                                {   "",
                                    "OPTIONS",
                                    "DESCRIBE",
                                    "ANNOUNCE",
                                    "SETUP",
                                    "PLAY",
                                    "PAUSE",
                                    "TEARDOWN",
                                    "GET_PARAMETER",
                                    "SET_PARAMETER",
                                    "REDIRECT",
                                    "RECORD",
                                    "EMBEDDED"};

static apr_byte_t     *server_ip;
static apr_port_t      server_port;
static apr_sockaddr_t *server_addr;
static apr_socket_t   *sock;
static apr_uint32_t    ip_type;
static apr_byte_t     *obj_name;
static apr_uint32_t    CSeq;
static apr_port_t      client_port_low;
static apr_port_t      client_port_up;
static apr_byte_t      session[100];


static apr_size_t add_header(apr_pool_t *p,apr_byte_t *buff, apr_int32_t choice)
{
    apr_size_t sz = strlen(HEADER_STRING[choice]);
    strcpy(buff, HEADER_STRING[choice]);
    strcpy(buff+sz, " rtsp://");
    sz += 8;
//    buff[sz++] = '[';
     memcpy(buff+sz, server_ip, strlen(server_ip));
    sz += strlen(server_ip);
    
  //   buff[sz++] = ']';
/*
    buff[sz++] = ':';
    memcpy(buff+sz,  apr_itoa(p,server_port), strlen(apr_itoa(p, server_port)));
    sz += strlen(apr_itoa(p, server_port));
*/
    buff[sz++] = '/';
    memcpy(buff+sz, obj_name, strlen(obj_name));
    sz += strlen(obj_name);
    memcpy(buff+sz, " RTSP/1.0\r\nCSeq: ",17);
    sz += 17;
    memcpy(buff+sz, apr_itoa(p,CSeq), strlen(apr_itoa(p, CSeq)));
    sz += strlen(apr_itoa(p, CSeq)) , CSeq++;
    buff[sz++] = '\r' ; buff[sz++] = '\n';
    return sz;  
}


static apr_status_t create_server(apr_pool_t *p)
{
    apr_status_t rv = apr_sockaddr_info_get(&server_addr, server_ip, APR_UNSPEC, server_port, 0, p);
    if(rv != APR_SUCCESS)
        puts(apr_psprintf(p, "%pm", &rv));
    return rv;
}    

static apr_status_t connect_to_server(apr_pool_t *p)
{
    apr_status_t rv = apr_socket_create(&sock, ip_type, SOCK_STREAM, APR_PROTO_TCP, p);
    if(rv != APR_SUCCESS){
        puts(apr_psprintf(p, "%pm", &rv));
        return rv;
    }
    rv = apr_socket_connect(sock, server_addr);
    if(rv != APR_SUCCESS){
        puts(apr_psprintf(p, "%pm", &rv));
        return rv;
    }
    return rv;
}

static apr_status_t get_session(apr_byte_t *buff,apr_size_t buff_sz)
{
    apr_size_t i = 0;
    for(i ; i < buff_sz-8 ; i++)
    {
        if(strncasecmp(buff+i,"session:",8) == 0)
            break;
    }
    if(i == buff_sz - 8)
        return !APR_SUCCESS;
    i += 8;
    while(buff[i] ==' ')
        i++;
    apr_size_t beg = 0;
    for(;;)
    {
        session[beg] = buff[i];
        beg++,i++;
        if(buff[i] == '\r' || buff[i] == '\n')
        {
            session[beg++] = '\0';
            break;
        }
    }
    i = beg - 1;
    while(buff[i] == ' ')
    {
        buff[i] = '\0';
        i--;
    }
    return APR_SUCCESS;
}



static apr_size_t option_data(apr_pool_t *p,apr_byte_t *buff)
{
   apr_size_t sz =  add_header(p, buff, OPTION);

    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n");
    buff[sz++] = '\r';buff[sz++] = '\n';
    return sz;
}

static apr_size_t describe_data(apr_pool_t *p,apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, DESCRIBE);
    memcpy(buff+sz,"Accept: applocation/sdp\r\n",25);
    sz += strlen("Accept: applocation/sdp\r\n");
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");
    return sz;
}

static apr_size_t announce_data(apr_pool_t *p,apr_byte_t *buff)
{

}

static apr_size_t setup_data(apr_pool_t *p,apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, SETUP); 
    fprintf(stdout,"Range of client port:\n");
    fprintf(stdout,"From :");
    fscanf(stdin,"%u", &client_port_low);
    fprintf(stdout,"to :");
    fscanf(stdin,"%d", &client_port_up);
    
    memcpy(buff+sz,"Transport: RTSP/AVP;unicast;client_port=",40);
    sz += 40;
    memcpy(buff+sz,apr_itoa(p, client_port_low), strlen(apr_itoa(p, client_port_low)));
    sz += strlen(apr_itoa(p, client_port_low));
    buff[sz++] = '-';
    memcpy(buff+sz,apr_itoa(p, client_port_up), strlen(apr_itoa(p, client_port_up)));
    sz += strlen(apr_itoa(p, client_port_up));
    buff[sz++] = '\r'; buff[sz++] = '\n';
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("Use    r-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");
    return sz;
}

static apr_size_t play_data(apr_pool_t *p, apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, PLAY); 
    memcpy(buff+sz, "Session: ", 9);
    sz += 9;
    strcpy(buff+sz, session);
    sz += strlen(session);
    buff[sz++] = '\r'; buff[sz++] = '\n';
 //   memcpy(buff+sz, "Range: npt= 0-", 14);
 //   sz += 14;
 //   buff[sz++] = '\r'; buff[sz++] = '\n';
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("Use    r-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");

    return sz;
}

static apr_size_t pause_data(apr_pool_t *p, apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, PAUSE); 
    memcpy(buff+sz, "Session: ", 9);
    sz += 9;
    strcpy(buff+sz, session);
    buff[sz++] = '\r' ; buff[sz++] = '\n';
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("Use    r-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");
    return sz;
}

static apr_size_t teardown_data(apr_pool_t *p,apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, TEARDOWN); 
    memcpy(buff+sz, "Session: ", 9);
    sz += 9;
    strcpy(buff+sz, session);
    buff[sz++] = '\r' ; buff[sz++] = '\n';
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("Use    r-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");
    return sz;

}

static apr_size_t get_parameter_data(apr_pool_t *p, apr_byte_t *buff)
{
    apr_size_t sz = add_header(p, buff, GET_PARAMETER);
    memcpy(buff+sz, "Session: ", 9);
    sz += 9;
    strcpy(buff+sz, session);
    buff[sz++] = '\r'; buff[sz++] = '\n';
    memcpy(buff+sz, "User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n",strlen("Use    r-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n"));
    sz += strlen("User-Agent: LibVLC/1.1.4 (LIVE555 Streaming Media v2010.04.09)\r\n\r\n");
    return sz;
}

static apr_size_t set_parameter_data(apr_pool_t *p, apr_byte_t *buff)
{

}

static apr_status_t recv_data(apr_uint32_t need_get_session)
{
    apr_pool_t *p;
    apr_pool_create(&p,NULL);
    apr_byte_t *buff = apr_palloc(p, 4096);
    apr_size_t buff_size = 4096;
    apr_status_t rv;
    rv = apr_socket_recv(sock, buff, &buff_size);
    if(rv != APR_SUCCESS){
        puts(apr_psprintf(p, "%pm", &rv));
        return rv;
    }
    apr_int32_t up = buff_size, i = 0;
    for(i = 0; i < up ; i++)
        fprintf(stdout,"%02x ", buff[i]);
    fprintf(stdout, "\n");

    for(i = 0; i < up ; i++)
        fprintf(stdout,"%c", buff[i]);
    fprintf(stdout, "\n");

    if(need_get_session)
        get_session(buff, buff_size);

    apr_pool_destroy(p);
    return rv;    
}

static apr_status_t send_data(apr_int32_t choice)
{
    apr_pool_t *p;
    apr_pool_create(&p,NULL);
    apr_byte_t *buff = apr_palloc(p, 4096);
    apr_size_t buff_size = 4096;
    switch(choice)
    {
        case OPTION:        buff_size = option_data(p, buff);       break;
        case DESCRIBE:      buff_size = describe_data(p, buff);     break;
        case ANNOUNCE:                                              break;
        case SETUP:         buff_size = setup_data(p, buff);        break;
        case PLAY:          buff_size = play_data(p, buff);         break;
        case PAUSE:         buff_size = pause_data(p,buff);         break;
        case TEARDOWN:      buff_size = teardown_data(p,buff);      break;
        case GET_PARAMETER: buff_size = get_parameter_data(p,buff); break;
        case SET_PARAMETER: buff_size = set_parameter_data(p,buff); break;
        default:            fprintf(stdout,"No such choice");       return APR_SUCCESS;
    }
    puts(buff);
    apr_status_t rv = apr_socket_send(sock,buff,&buff_size);
    if(rv != APR_SUCCESS)
        return rv;
    puts(buff);
    //rv = apr_socket_send(sock, buff, &buff_size);
    if(choice == SETUP)
        recv_data(NEED_GET_SESSION);
    else
        recv_data(NEED_NOT_GET_SESSION);
    apr_pool_destroy(p);
        
}


static void menu()
{
    fprintf(stdout,"********************************************\n");
    fprintf(stdout,"\t1.\tsend \"OPTIONS\"\n");
    fprintf(stdout,"\t2.\tsend \"DESCRIBE\"\n");
    fprintf(stdout,"\t3.\tsend \"ANNOUNCE(?)\"\n");
    fprintf(stdout,"\t4.\tsend \"SETUP\"\n");
    fprintf(stdout,"\t5.\tsend \"PLAY\"\n");
    fprintf(stdout,"\t6.\tsend \"PAUSE\"\n");
    fprintf(stdout,"\t7.\tsend \"TEARDOWN\"\n");
    fprintf(stdout,"\t8.\tsend \"GET_PARAMETER(?)\"\n");
    fprintf(stdout,"\t9.\tsend \"SET_PARAMETER(?)\"\n");
    fprintf(stdout,"********************************************\n");
}

int main(int argc,char *argv[])
{

    if(argc < 5)
    {
        puts("usage: server_ip server_port ip_type(4/6) obj_name");
        return 0;    
    }
    apr_pool_t *p;
    apr_status_t rv;
   
    apr_initialize();

    apr_pool_create(&p, NULL);    

    server_ip = apr_pstrdup(p,argv[1]);
    server_port = atoi(argv[2]);
    CSeq = 1;

    rv = create_server(p);
    if(rv != APR_SUCCESS)
        return 0;
    
    if(argv[3][0] == '4')
        ip_type = APR_INET;
    else
        ip_type = APR_INET6;
    rv = connect_to_server(p);
    if(rv != APR_SUCCESS)
        return 0;
/*
    if(ip_type == APR_INET6){
        apr_byte_t *tmp_server_ip = apr_pstrcat(p, "[", server_ip, "]" , NULL);
        strcpy(server_ip,tmp_server_ip);
    }
*/
    obj_name = apr_pstrdup(p, argv[4]);
    apr_int32_t choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        send_data(choice);
    }

    return 0;
}
